﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using MoneyWentWhere.WinFormsUI.Properties;
using MoneyWentWhere.DataModel;
using MoneyWentWhere.DataModel.Storage;
using System.Windows.Forms;
using MoneyWentWhere.Utils.Storage;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using MoneyWentWhere.Utils;

namespace MoneyWentWhere.WinFormsUI
{
    class FileManager : INotifyPropertyChanged
    {
        public FileManager()
        {
            // Open the doc on startup, or create a new one
            if (!String.IsNullOrEmpty(OpenOnStartup_Filename))
            {
                // Try to open a designated document on startup
                if (TryOpenDocument(OpenOnStartup_Filename))
                {
                    OpenOnStartup = true;
                }
            }

            // No file to open, or opening failed
            if (Document == null)
            {
                NewDocument();
            }
        }

        /// <summary>
        /// Gets or sets the current document
        /// </summary>
        public MoneyDataSet Document
        {
            get { return _document; }
            set
            {
                value.ArgNotNull("value");
                if (_document == value) { return; }

                // Dispose old
                if (_document != null) 
                {
                    ((IDisposable)_document).Dispose();
                }

                _document = value;
                OnPropertyChanged();
            }
        }
        MoneyDataSet _document;

        /// <summary>
        /// Open the current document on next startup of the app.
        /// </summary>
        public bool OpenOnStartup
        {
            get { return _openOnStartup; }
            set
            {
                _openOnStartup = value;

                if (_openOnStartup)
                {
                    if (Filename != null) { OpenOnStartup_Filename = Filename; }
                }
                else 
                {
                    if (OpenOnStartup_Filename == Filename) { OpenOnStartup_Filename = null; }
                }

                OnPropertyChanged();
            }
        }        
        bool _openOnStartup = false;

        /// <summary>
        /// Name of the file to open on startup, null if none.
        /// </summary>
        String OpenOnStartup_Filename
        {
            get { return Settings.Default.OpenOnStartup_Filename; }
            set
            {
                Settings.Default.OpenOnStartup_Filename = value;
                Settings.Default.Save();
            }
        }

        public String Filename
        {
            get
            {
                if (Document == null) { return null; }
                return Document.StorageInfo.Filename;
            }
            set
            {
                // No need for change
                String oldFilename = Filename;
                if (value == Filename) { return; }

                Document.StorageInfo.Filename = value;

                if (OpenOnStartup && oldFilename == null)
                {
                    // Special case: file was not named before, but open on startup
                    // option was selected. Save the new name to open.
                    OpenOnStartup = true;
                }
                else
                {
                    // Normal case: set the open on startup option 
                    // if file name matches the current file
                    OpenOnStartup = Document.StorageInfo.Filename != null &&
                        Document.StorageInfo.Filename == OpenOnStartup_Filename;
                }

                // Affected properties
                OnPropertyChanged();
            }
        }

        public String Password
        {
            get
            {
                if (Document == null) { return null; }
                return Document.StorageInfo.Password;
            }
            set
            {
                // No need for change
                String oldPassword = Password;
                if (value == oldPassword) { return; }

                Document.StorageInfo.Password = value;

                // If file is on disk, save immediately, for security reasons
                // File format choice will be done in SaveDocument()
                if (Filename != null)
                {
                    TrySaveDocument();
                }

                OnPropertyChanged();
            }
        }

        public void NewDocument()
        {
            Document = new MoneyDataSet();
            OnPropertyChanged("Filename");
            OnPropertyChanged("Password");
        }

        /// <summary>
        /// Try to open a document, returning true on success
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public bool TryOpenDocument(String filename, String password = null)
        {
            filename.ArgNotNullOrEmpty("filename");

            while (true)
            {
                try
                {
                    Document = MoneyDataSet.Load(filename, password);

                    OpenOnStartup = Document.StorageInfo.Filename != null &&
                                    Document.StorageInfo.Filename == OpenOnStartup_Filename;

                    OnPropertyChanged("Filename");
                    OnPropertyChanged("Password");

                    return true;
                }
                catch (InvalidPasswordException)
                {
                    // Ask for password
                    password = InputBox.GetText(
                        Resources.enterPasswordTitle,
                        String.Format(Resources.enterPasswordPrompt, filename),
                        isPassword: true);

                    if (password == null) { return false; }
                }
                catch (DirectoryNotFoundException)
                {
                    Service.Dialog.ShowMessage("File not found: {0}", Path.GetFullPath(filename));
                    return false;
                }
                catch (FileNotFoundException)
                {
                    Service.Dialog.ShowMessage("File not found: {0}", Path.GetFullPath(filename));
                    return false;
                }
#if DEBUG
#else
                catch (Exception ex2)
                {
                    Service.Dialog.ShowMessage("Cannot load file.\r\nError: {0}", ex2.Message);
                    return false;
                }
#endif
            }
        }

        public bool TrySaveDocument()
        {
            Document.ArgNotNull("Document");
            Document.StorageInfo.Filename.ArgNotNullOrEmpty("Document.StorageInfo.Filename");

            // Change format if needed
            var oldFormat = Document.StorageInfo.DataFormat;
            var newFormat = MwwFileFormats.GetSaveFileFormat(
                Document.StorageInfo.Filename, Document.StorageInfo.Password);

            if (oldFormat != newFormat)
            {
                Document.StorageInfo.DataFormat = newFormat;

                // TODO: if changing from plaintext format, wipe the file
                // details of actual wiping are difficult
            }

            try
            {
                Document.Save();
                return true;
            }
            catch (Exception e)
            {
                Service.Dialog.ShowMessage("Cannot save file: {0}\r\nError: {1}", Filename, e.Message);
                return false;
            }
        }

        private void OnPropertyChanged([CallerMemberName] String caller = null)
        {
            if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(caller)); }
        }


        public event PropertyChangedEventHandler PropertyChanged;
    }
}
